import CesiumApp from './CesiumApp';
import VGE from '../../VGE';
import { saveViewUndo } from './Utils/cesium-view-record';

export class Camera {
	constructor(viewer, iCesium) {
		this.viewer = viewer;
		this.iCesium = iCesium;
	}

	/**
	 * 查询相机参数
	 * @param original {boolean} 是否获取引擎原参数
	 * @returns {Promise<{heading: *, lon: *, pitch: *, lat: *, height: *}>}
	 */
	async getCameraParams(original = true) {
		try {
			const { position, heading, pitch } = this.viewer.camera;
			const { longitude, latitude, height } = this.iCesium?.Cartographic.fromCartesian(position);

			const toDegrees = (val) => (val * 180) / Math.PI;

			const result = {
				lon: toDegrees(longitude),
				lat: toDegrees(latitude),
				height: height,
				heading: toDegrees(heading),
				pitch: toDegrees(pitch)
			};

			if (!original) {
				result.heading = 360 - result.heading;
				result.pitch = result.pitch + 90;
				result.original = 'VGE-CAMERA';
			}

			return result;
		} catch (e) {
			console.log(e.message);
		}
	}

	/**
	 * 飞到给定视角
	 * @param cameraParams {object} 视角参数
	 * @param duration {number} 飞行时间
	 * @param original {boolean} 是否引擎原参数
	 * @param record {boolean} 是否记录到历史视图
	 * @returns {Promise<void>}
	 */
	async flyTo(cameraParams, duration = 1, original = true, record = true) {
		if (!VGE.util.validateCameraParams(cameraParams)) return;
		try {
			const before = await this.getCameraParams();

			let { lon, lat, height, heading, pitch } = cameraParams;
			const destination = this.iCesium?.Cartesian3.fromDegrees(lon, lat, height);
			const toRadians = (val) => (val * Math.PI) / 180;

			if (cameraParams.original === 'VGE-CAMERA') {
				heading = 360 - heading;
				pitch = pitch - 90;
			}

			this.viewer.camera.flyTo({
				destination,
				orientation:
					{
						heading: toRadians(heading),
						pitch: toRadians(pitch),
						roll: 0
					} || {},
				duration,
				complete: async () => {
					const after = await this.getCameraParams();
					record && saveViewUndo(before, after);
				}
			});
		} catch (e) {
			console.log(e.message);
		}
	}

	/**
	 * 主视角
	 * @returns {Promise<void>}
	 */
	async homeView() {
		this.flyTo(CesiumApp.homeViewParams);
	}

	/**
	 * 顶视图
	 * @returns {Promise<void>}
	 */
	async topView() {
		const params = Object.assign({}, CesiumApp.homeViewParams);
		params.pitch = -90;
		this.flyTo(params);
	}

	async zoomIn(factor = 0.6, duration = 1, point) {
		// 屏幕坐标转世界坐标
		const scene = this.viewer.scene;
		const container = this.viewer._container;
		const x = container.clientWidth / 2;
		const y = container.clientHeight / 2;
		const ray = this.viewer.camera.getPickRay(new this.iCesium.Cartesian2(x, y));
		const centerPoint = scene.globe.pick(ray, scene);
		// Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, cartesian3);
		point = point || centerPoint;

		factor = Math.abs(1 - factor) % 1;
		try {
			const before = await this.getCameraParams();

			if (point) {
				const camera = this.viewer.scene.camera;
				const cartesian3Scratch = new this.iCesium.Cartesian3();
				var direction = this.iCesium?.Cartesian3.subtract(
					camera.position,
					point,
					cartesian3Scratch
				);
				var movementVector = this.iCesium?.Cartesian3.multiplyByScalar(
					direction,
					factor,
					direction
				);
				var endPosition = this.iCesium?.Cartesian3.add(point, movementVector, point);
				return camera.flyTo({
					destination: endPosition,
					orientation: {
						direction: camera.direction,
						up: camera.up
					},
					duration: duration,
					complete: async () => {
						const after = await this.getCameraParams();
						saveViewUndo(before, after);
					}
				});
			}

			const { position, heading, pitch } = this.viewer.camera;
			const { longitude, latitude, height } = this.iCesium?.Cartographic.fromCartesian(position);
			const toDegrees = (val) => (val * 180) / Math.PI;
			const destination = this.iCesium?.Cartesian3.fromDegrees(
				toDegrees(longitude),
				toDegrees(latitude),
				height * factor
			);

			this.viewer.camera.flyTo({
				destination,
				orientation:
					{
						heading,
						pitch,
						roll: 0
					} || {},
				duration,
				complete: async () => {
					const after = await this.getCameraParams();
					saveViewUndo(before, after);
				}
			});
		} catch (e) {
			console.error(e.message);
		}
	}

	async zoomOut(factor = 1.6, duration = 1, point) {
		// 屏幕坐标转世界坐标
		const scene = this.viewer.scene;
		const container = this.viewer._container;
		const x = container.clientWidth / 2;
		const y = container.clientHeight / 2;
		const ray = this.viewer.camera.getPickRay(new this.iCesium.Cartesian2(x, y));
		const centerPoint = scene.globe.pick(ray, scene);
		// Cesium.SceneTransforms.wgs84ToWindowCoordinates(scene, cartesian3);
		point = point || centerPoint;

		factor = 1 + (factor % 1);
		try {
			const before = await this.getCameraParams();

			if (point) {
				const camera = this.viewer.scene.camera;
				const cartesian3Scratch = new this.iCesium.Cartesian3();
				const direction = this.iCesium?.Cartesian3.subtract(
					camera.position,
					point,
					cartesian3Scratch
				);
				const movementVector = this.iCesium?.Cartesian3.multiplyByScalar(
					direction,
					factor,
					direction
				);
				const endPosition = this.iCesium?.Cartesian3.add(point, movementVector, point);

				return camera.flyTo({
					destination: endPosition,
					orientation: {
						direction: camera.direction,
						up: camera.up
					},
					duration: duration,
					convert: false,
					complete: async () => {
						const after = await this.getCameraParams();
						saveViewUndo(before, after);
					}
				});
			}

			const { position, heading, pitch } = this.viewer.camera;
			const { longitude, latitude, height } = this.iCesium?.Cartographic.fromCartesian(position);
			const toDegrees = (val) => (val * 180) / Math.PI;
			const destination = this.iCesium?.Cartesian3.fromDegrees(
				toDegrees(longitude),
				toDegrees(latitude),
				height * factor
			);

			this.viewer.camera.flyTo({
				destination,
				orientation:
					{
						heading,
						pitch,
						roll: 0
					} || {},
				duration,
				complete: async () => {
					const after = await this.getCameraParams();
					saveViewUndo(before, after);
				}
			});
		} catch (e) {
			console.error(e.message);
		}
	}

	async maxZoomDistance(distance) {
		const controller = this.viewer.scene.screenSpaceCameraController;
		controller.maximumZoomDistance = distance;
	}

	async minZoomDistance(distance) {
		const controller = this.viewer.scene.screenSpaceCameraController;
		controller.minimumZoomDistance = distance;
	}

	async redo() {
		if (this.viewer.forwardStack.length === 0 || this.viewer._currentBaseline === undefined) return;
		this.viewer.backStack.push(this.viewer._currentBaseline);
		const cam = (this.viewer._currentBaseline = this.viewer.forwardStack.pop());

		if (VGE.util.validateCameraParams(cam)) this.flyTo(cam, 0.6, true, false);
	}

	async undo() {
		if (this.viewer.backStack.length === 0 || this.viewer._currentBaseline === undefined) return;
		this.viewer.forwardStack.push(this.viewer._currentBaseline);
		const cam = (this.viewer._currentBaseline = this.viewer.backStack.pop());

		if (VGE.util.validateCameraParams(cam)) this.flyTo(cam, 0.6, true, false);
	}
}
